#include "glwindow.h"
#include "src/gui/textprinter.h"
#include "src/utils/displaylists.h"
#include "common/be_event_system.h"

#include <GL/glew.h>

GLWindow::GLWindow()
{
	// register an exit event from glwindow
	Execcmd execcmd;
	BeEventSystem::Instance()->registerEvent( "glwindow-quit", boost::shared_ptr<Event>(new Event(execcmd.gen("quit"), EVENT_NOREPEAT, 0)) );	
}

void GLWindow::create(const char* const title, const unsigned int width, const unsigned int height)
{
	if( SDL_Init(SDL_INIT_VIDEO) < 0 )
	{
		std::cerr << "Video initialization failed: " << SDL_GetError() << std::endl;
		exit(1);
	}

	Settings * const settingsPointer=Settings::Instance();
	if(settingsPointer)
	{
		Settings& settings=*settingsPointer;
		if(settings.getCVar("window_safemode")==0)
		{
			const int width=settings.getCVar("window_width");
			const int height=settings.getCVar("window_height");
			const int colorBits=settings.getCVar("window_color_bits");
			const int depthBits=settings.getCVar("window_depth_bits");
			const int stencilBits=settings.getCVar("window_stencil_bits");
			const int multisamples=settings.getCVar("window_multisamples");
			const int doublebuffer=settings.getCVar("window_doublebuffer");
			const int vsync=settings.getCVar("window_vsync");
			const int resizable=settings.getCVar("window_resizable");
			const int fullscreen=settings.getCVar("window_fullscreen");
			const int hardwareGl=settings.getCVar("window_hardware_gl");

			if(colorBits==32||colorBits==24)
			{
				SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
				SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
				SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
				SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
			}
			else
			{
				SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 4);
				SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 4);
				SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 4);
				SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 4);
			}

			SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, depthBits);
			SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencilBits);

			if(multisamples>1)
			{
				SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
				SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, multisamples);
			}
			else
			{
				SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
				SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
			}

			SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, doublebuffer);
			SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, vsync);

			Uint32 videoFlags = SDL_OPENGL;

			if(resizable)
			{
				videoFlags |= SDL_RESIZABLE;
			}

			if(fullscreen)
			{
				videoFlags |= SDL_FULLSCREEN;
			}

			SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, hardwareGl);

			//Hacky
			w_bpp = colorBits;
			w_width = width;
			w_height = height;
			n_width = width;
			n_height = height;

			Settings::Instance()->winWidth = &w_width;
			Settings::Instance()->winHeight = &w_height;

			//Set window title
			SDL_WM_SetCaption(title, 0);

			//Create window
			surface = SDL_SetVideoMode(w_width, w_height, w_bpp, videoFlags);
			glewInit();

			std::cerr << "SDL: subsystem initialized\n";

			//Enable GL multisampled rendering if required
			int value;
			SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &value);
			if(value)
			{
				glEnable(GL_MULTISAMPLE);
				glHint(GL_LINE_SMOOTH_HINT, GL_NICEST );
				glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST );
				glEnable(GL_LINE_SMOOTH);
				glEnable(GL_POLYGON_SMOOTH);
			}

			//Hack
			settings.setCVar("fullscreen", fullscreen);
			settingsfs = settings.getCVarPtr("fullscreen");
			fs = *settingsfs;

			return;

		}
	}
	createSafe(title, width, height);
}

void GLWindow::createSafe( const char* const title, const unsigned int width, const unsigned int height )
{
	vidInfo = SDL_GetVideoInfo();

	w_bpp = vidInfo->vfmt->BitsPerPixel;

	if ( !vidInfo )
	{
		std::cerr << "Cannot get video information: " <<  SDL_GetError() << std::endl;
		exit(1);
	}

	vidFlags = SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE;

	hwaccel = false;
	if( vidInfo->hw_available )
	{
		hwaccel = true;
		vidFlags |= SDL_HWSURFACE;
	}
	else
		vidFlags |= SDL_SWSURFACE;

	if( vidInfo->blit_hw != 0 )
		vidFlags |= SDL_HWACCEL;

	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 4);
	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 4);
	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 4);
	SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 4);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

	SDL_WM_SetCaption(title, 0);
	// 	SDL_WM_SetIcon(SDL_LoadBMP("other files/critterding icon.png"), 0);
	// 	SDL_WM_SetIcon(SDL_LoadBMP("other files/cd.bmp"), 0);
	// 	string pixmappath = Settings::Instance()->binarypath;
	// 	pixmappath.append( "../share/critterding/pixmaps/cd.bmp" );
	// 	SDL_WM_SetIcon(SDL_LoadBMP(pixmappath.c_str()), 0);
	// 	SDL_WM_SetIcon(SDL_LoadBMP("/projects/lessons/lesson20/data/image2.bmp"), 0);

	w_width = width;
	w_height = height;
	n_width = width;
	n_height = height;

	Settings::Instance()->winWidth = &w_width;
	Settings::Instance()->winHeight = &w_height;

	settingsfs = Settings::Instance()->getCVarPtr("fullscreen");

	fs = *settingsfs;

	if ( fs == 1 )
		surface = SDL_SetVideoMode( w_width, w_height, w_bpp, vidFlags | SDL_FULLSCREEN );
	else
		surface = SDL_SetVideoMode( w_width, w_height, w_bpp, vidFlags | SDL_RESIZABLE );
	glewInit();
	std::cerr << "SDL: subsystem initialized\n";
	// 	std::cerr << "Video " << front.width() << "x" << front.height() << "x" << int(front.getSurface()->format->BitsPerPixel) << "\n";
	// 	std::cerr << "Render Mode: " <<  ((hwaccel) ? "Direct Rendering" : "Software Rendering")   << "\n";
	// 	std::cerr << "Hardware Blit Acceleration: " << ((vidInfo->blit_hw) ? "Yes": "No") << "\n";
}

void GLWindow::resize()
{
	if ( w_height == 0 ) w_height = 1;
	if ( w_width == 0 ) w_width = 1;

#ifndef _WIN32
	SDL_FreeSurface(surface);
	surface = SDL_SetVideoMode( w_width, w_height, w_bpp, vidFlags | SDL_RESIZABLE );
	glewInit();
#endif
}

void GLWindow::toggleFs()
{
	SDL_FreeSurface(surface);
	Uint32 videoFlags=vidFlags;
	if ( fs )
	{
		if ( w_height == 0 ) w_height = 1;
		if ( w_width == 0 ) w_width = 1;
		n_width = w_width;
		n_height = w_height;
		w_width = Settings::Instance()->getCVar("fsX");
		w_height = Settings::Instance()->getCVar("fsY");
		videoFlags |= SDL_FULLSCREEN;
	}
	else
	{
		w_width = n_width;
		w_height = n_height;
		videoFlags |= SDL_RESIZABLE;
	}
	surface = SDL_SetVideoMode( w_width, w_height, w_bpp, videoFlags );
	glewInit();
	Displaylists::Instance()->generateList();
	Textprinter::Instance()->setUpFonts();
}

void GLWindow::process()
{
	if ( fs != *settingsfs )
	{
		fs = *settingsfs;
		toggleFs();
	}

	while( SDL_PollEvent(&event) )
	{
		if(event.type == SDL_VIDEORESIZE)
		{
			w_width = event.resize.w;
			w_height = event.resize.h;
			resize();
		}

		else if(event.type == SDL_KEYDOWN)
		{
			BeEventSystem::Instance()->activateKeystate( event.key.keysym.sym );
		}
		else if(event.type == SDL_KEYUP)
		{
			BeEventSystem::Instance()->deactivateKeystate( event.key.keysym.sym );
		}

		else if (event.type == SDL_MOUSEBUTTONDOWN)
		{
			BeEventSystem::Instance()->activateKeystate( event.button.button+2048 );
		}
		else if (event.type == SDL_MOUSEBUTTONUP)
		{
			BeEventSystem::Instance()->deactivateKeystate( event.button.button+2048 );
		}

		else if (event.type == SDL_JOYBUTTONDOWN)
		{
			BeEventSystem::Instance()->activateKeystate( event.button.button+4096 );
		}
		else if (event.type == SDL_JOYBUTTONUP)
		{
			BeEventSystem::Instance()->deactivateKeystate( event.button.button+4096 );
		}

		else if(event.type == SDL_JOYAXISMOTION)
		{
			BeEventSystem::Instance()->setAxisstate( event.jaxis.axis+6144, event.jaxis.value );
		}

		else if(event.type == SDL_MOUSEMOTION)
		{
			BeEventSystem::Instance()->setAxisstate( 0+7168, event.motion.x );
			BeEventSystem::Instance()->setAxisstate( 1+7168, event.motion.y );
		}

		else if(event.type == SDL_QUIT)
		{
			BeEventSystem::Instance()->activateKeystate( "glwindow-quit" );
		}
	}
}

// void GLWindow::runScene(BeScene* scene)
// {
// 	while( !scene->exitscene )
// 	{
// 		if ( fs != *settingsfs )
// 		{
// 			fs = *settingsfs;
// 			toggleFs();
// // 			scene->canvas.updateAbsPosition();
// 		}
// 
// 		while(SDL_PollEvent(&event))
// 		{
//                         if(event.type == SDL_VIDEORESIZE)
// 			{
// 				w_width = event.resize.w;
// 				w_height = event.resize.h;
//                                 resize();
// // 				scene->canvas.updateAbsPosition();
// 			}
// 
// 			else if(event.type == SDL_KEYDOWN)
// 			{
// 				BeEventSystem::Instance()->activateKeystate( event.key.keysym.sym );
// 			}
// 			else if(event.type == SDL_KEYUP)
// 			{
// 				BeEventSystem::Instance()->deactivateKeystate( event.key.keysym.sym );
// 			}
// 
// 			else if (event.type == SDL_MOUSEBUTTONDOWN)
// 			{
// 				BeEventSystem::Instance()->activateKeystate( event.button.button+2048 );
// 			}
// 			else if (event.type == SDL_MOUSEBUTTONUP)
// 			{
// 				BeEventSystem::Instance()->deactivateKeystate( event.button.button+2048 );
// 			}
// 
// 			else if (event.type == SDL_JOYBUTTONDOWN)
// 			{
// 				BeEventSystem::Instance()->activateKeystate( event.button.button+4096 );
// 			}
// 			else if (event.type == SDL_JOYBUTTONUP)
// 			{
// 				BeEventSystem::Instance()->deactivateKeystate( event.button.button+4096 );
// 			}
// 
// 			else if(event.type == SDL_JOYAXISMOTION)
// 			{
// 				BeEventSystem::Instance()->setAxisstate( event.jaxis.axis+6144, event.jaxis.value );
// 			}
// 
// 			else if(event.type == SDL_MOUSEMOTION)
// 			{
// 				BeEventSystem::Instance()->setAxisstate( 0+7168, event.motion.x );
// 				BeEventSystem::Instance()->setAxisstate( 1+7168, event.motion.y );
// 			}
// 
// 			else if(event.type == SDL_QUIT)
// 				scene->exitscene = true;
// 		}
// 
// 		scene->draw();
// 	}
// 	SDL_FreeSurface(surface);
//         SDL_Quit();
// }

GLWindow::~GLWindow()
{
	std::cerr << "closing glwindow" << std::endl;
	SDL_FreeSurface(surface);
        SDL_Quit();
}


